/* Octopus PDF - A simple document viewer for Android.
   Copyright (C) 2013  Gerald Thaler

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
package de.geraldthaler.octopuspdf.control;

import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
import android.os.Bundle;
import android.view.MotionEvent;
import de.geraldthaler.octopuspdf.MainActivity;
import de.geraldthaler.octopuspdf.docrender.DocRenderClient;
import de.geraldthaler.octopuspdf.docrender.DocRenderer;
import de.geraldthaler.octopuspdf.func.ProcBoolean;
import de.geraldthaler.octopuspdf.func.ProcInt;
import de.geraldthaler.octopuspdf.memory.DirectByteBufferPool;
import de.geraldthaler.octopuspdf.view.Fling;
import de.geraldthaler.octopuspdf.view.FpsMeter;
import de.geraldthaler.octopuspdf.view.PageModeEvent;
import de.geraldthaler.octopuspdf.view.PageNrEvent;
import de.geraldthaler.octopuspdf.view.PasswordEvent;
import de.geraldthaler.octopuspdf.view.TexturePool;
import de.geraldthaler.octopuspdf.view.Touch;

/**
 * @author Gerald Thaler
 */
public final class DocControl implements Control, DocRenderClient {
    public static final int PAGE_MODE_ONE_PAGE = 0;
    public static final int PAGE_MODE_TWO_PAGES_WITHOUT_TITLE = 1;
    public static final int PAGE_MODE_TWO_PAGES_WITH_TITLE = 2;

    private static final float COORD_ADJUST_SPEED = 5e-2f;
    private static final float MOTION_RESISTANCE = 0.1f;
    private static final float MOTION_RESISTANCE_START = 1f / MOTION_RESISTANCE;
    private static final float POS_ADJUST_SPEED = 0.5f;
    private static final float MINIMUM_FPS = 45f;
    private static final float MIN_TOUCH_RADIUS = 16f;
    private static final float MINIMUM_HEIGHT = 256f;
    private static final float MAX_FLING_DURATION = 0.5f;
    private static final float MIN_FLING_VELOCITY = 1000f;
    
    private static float adjustCoord(final float c, final float target) {
        return adjustValue(c, target, COORD_ADJUST_SPEED);
    }

    private static float adjustPos(final float c, final float target) {
        return adjustValue(c, target, POS_ADJUST_SPEED);
    }

    private static float adjustValue(final float c, final float target, final float speed) {
        final float d = target - c;
        if (d >= -1f && d <= 1f)
            return target;
        return c + d * speed;
    }

    private static float adjustValueInt(final float c, final float target, final float speed) {
        float d = target - c;
        if (d >= -1f && d <= 1f)
            return target;
        d = d * speed;
        if (d > -1f && d < 1f)
            d = d >= 0 ? 1f : -1f;
        return c + d;
    }

    private int pageMode = PAGE_MODE_ONE_PAGE;
    
	private final MainActivity activity;
    private final DirectByteBufferPool directByteBufferPool;
    private final FpsMeter fpsMeter;
    private int glGeneration;
	private boolean authenticated = false;
	private int passwordRequest = 0;
	private int passwordResponse = 0;
    private int pageCount = -1;
	private boolean knownIfPasswordNeeded = false;
	private boolean pageCountRequested = false;
	private boolean passwordNeededRequested = false;
    private final DocRenderer docRenderer;
    private float surfaceWidth, surfaceHeight;
    private final TexturePool texturePool;
    private float xPos, yPos, xPosDraw, yPosDraw;

    private float adjustCountRemainder = 0f;
    private final boolean haveSavedState;
    private final Sheet[] sheets = new Sheet[3];
    private int pageNr = 0;
    private boolean touched = false;
    private float turnLeftProgress = 0f, turnLeftProgressDraw = 0f;
    private float turnRightProgress = 0f, turnRightProgressDraw = 0f;
    private float savedXPos, savedYPos;
    private float savedHeight;
    private float width, height;
    private float prevTouchCenterX = 0f, prevTouchCenterY = 0f, prevTouchRadius = 0f;
    private float touchCenterX = 0f, touchCenterY = 0f, touchRadius = 0f;
    private boolean lastTouchWasMove = false;
    private float flingLeft = 0f, flingRight = 0f;

    public DocControl(final MainActivity activity, final TexturePool texturePool,
    		final DirectByteBufferPool directByteBufferPool, final DocRenderer docRenderer, final FpsMeter fpsMeter,
    		final Bundle state, final int pageMode) {
    	this.activity = activity;
        this.texturePool = texturePool;
        this.directByteBufferPool = directByteBufferPool;
        this.docRenderer = docRenderer;
        this.fpsMeter = fpsMeter;
        this.pageMode = pageMode;
        haveSavedState = state != null;
        if (haveSavedState) {
            pageNr = max(state.getInt("pageNr"), 0);
            this.pageMode = state.getInt("pageMode");
            savedXPos = state.getFloat("xPos");
            savedYPos = state.getFloat("yPos");
            savedHeight = state.getFloat("height");
        }
    }

    @Override
    public void dispose() {
        for (final Sheet sheet : sheets) {
            if (sheet != null)
                sheet.dispose();
        }
        docRenderer.cancelAll(this);
        docRenderer.dispose();
    }

    @Override
    public boolean onDrawFrame() {
    	if (!knownIfPasswordNeeded) {
            requestPasswordNeeded();
            return false;
    	}
    	if (!authenticated) {
    		requestPassword();
    		return false;
    	}
        if (pageCount < 0) {
            requestPageCount();
            return false;
        }
        width = sheets[1].getWidth();
        height = sheets[1].getHeight();
        final float frameDuration = fpsMeter.getFrameDuration();
        boolean adjusted = false;
        if (flingLeft > 0f) {
            turnLeftProgress = adjustCoord(turnLeftProgress, surfaceWidth);
            turnLeftProgressDraw = adjustCoord(turnLeftProgressDraw, surfaceWidth);
            checkForTurn();
            adjusted = true;
        }
        if (flingRight > 0f) {
            turnRightProgress = adjustCoord(turnRightProgress, surfaceWidth);
            turnRightProgressDraw = adjustCoord(turnRightProgressDraw, surfaceWidth);
            checkForTurn();
            adjusted = true;
        }
        final float leftBrightness, rightBrightness;
        if (turnLeftProgressDraw <= 0f)
            leftBrightness = 1f;
        else
            leftBrightness = 1f - 2f * turnLeftProgressDraw / surfaceWidth;
        if (turnRightProgressDraw <= 0f)
            rightBrightness = 1f;
        else
            rightBrightness = (surfaceWidth - 2f * turnRightProgressDraw) / surfaceWidth;
        final float brightness = max(min(leftBrightness, rightBrightness), 0f);
        sheets[1].draw(xPosDraw, yPosDraw, brightness, 0f, 0f, 0f);
        if (turnLeftProgressDraw > 0f) {
            final float w = sheets[0].getWidth();
            final float h = sheets[0].getHeight();
            final float rotate = max(15f * leftBrightness, 0f);
            sheets[0].draw(turnLeftProgressDraw - (w + surfaceWidth) * 0.5f, 0f, 1f, rotate, w * 0.5f, h * 0.5f);
        }
        if (turnRightProgressDraw > 0f) {
            final float w = sheets[2].getWidth();
            final float h = sheets[2].getHeight();
            final float rotate = max(15f * rightBrightness, 0f);
            sheets[2].draw((w + surfaceWidth) * 0.5f - turnRightProgressDraw, 0f, 1f, rotate, w * -0.5f, h * -0.5f);
        }
        final float adjustCount = max(frameDuration * MINIMUM_FPS, 1f) + adjustCountRemainder;
        final int adjustCountInt = (int) adjustCount;
        adjustCountRemainder = adjustCount - adjustCountInt;
        for (int n = 0; n < adjustCount; ++n) {
            if (adjust())
                adjusted = true;
        }
        return adjusted;
    }

    @Override
    public void onEvent(final Object event) {
    	if (event instanceof PasswordEvent) {
    		authenticate(((PasswordEvent) event).getPassword());
    		return;
    	}
    	if (event instanceof PageNrEvent) {
    		if (pageCount < 0)
    			return;
    		final int oldPageNr = pageNr;
    		pageNr = normalizePageNr(((PageNrEvent) event).getPageNr());
    		if (pageNr == oldPageNr)
    			return;
    		for (int n = 0; n <= 2; ++n) {
	            if (sheets[n] != null) {
	                sheets[n].dispose(glGeneration);
	                sheets[n] = null;
	            }
    		}
            informActivityAboutPageNr();
            createSheets();
            for (int n = 0; n <= 2; ++n) {
            	if (sheets[n] != null)
            		sheets[n].setHeight(surfaceHeight);
            }
            if (pageNr > oldPageNr) {
	            xPos = surfaceWidth;
	            xPosDraw = surfaceWidth;
            } else {
	            xPos = -surfaceWidth;
	            xPosDraw = -surfaceWidth;
            }
            yPos = 0;
            yPosDraw = 0;
            turnLeftProgress = 0f;
            turnLeftProgressDraw = 0f;
            turnRightProgress = 0f;
            turnRightProgressDraw = 0f;
            flingLeft = 0f;
            flingRight = 0f;
    		return;
    	}
    	if (event instanceof PageModeEvent) {
    		if (pageCount < 0)
    			return;
    		pageMode = ((PageModeEvent) event).getPageMode();
    		pageNr = normalizePageNr(pageNr);
    		for (int n = 0; n <= 2; ++n) {
	            if (sheets[n] != null) {
	                sheets[n].dispose(glGeneration);
	                sheets[n] = null;
	            }
    		}
            createSheets();
            for (int n = 0; n <= 2; ++n) {
            	if (sheets[n] != null)
            		sheets[n].setHeight(surfaceHeight);
            }
            xPos = surfaceWidth;
            xPosDraw = surfaceWidth;
            yPos = 0;
            yPosDraw = 0;
            turnLeftProgress = 0f;
            turnLeftProgressDraw = 0f;
            turnRightProgress = 0f;
            turnRightProgressDraw = 0f;
            flingLeft = 0f;
            flingRight = 0f;
    		return;
    	}
        if (pageCount < 0)
            return;
        if (event instanceof Touch)
            onTouch((Touch) event);
        else if (event instanceof Fling)
            onFling((Fling) event);
    }

	@Override
    public void onSurfaceChanged(final int width, final int height) {
        surfaceWidth = width;
        surfaceHeight = height;
    }

    @Override
    public void onSurfaceCreated(final int glGeneration) {
        this.glGeneration = glGeneration;
        for (final Sheet sheet : sheets) {
            if (sheet != null)
                sheet.onSurfaceCreated(glGeneration);
        }
    }

    public void saveInstanceState(final Bundle state) {
        state.putInt("pageNr", pageNr);
        state.putInt("pageMode", pageMode);
        state.putFloat("xPos", xPos);
        state.putFloat("yPos", yPos);
        final float height = sheets[1] == null ? surfaceHeight : sheets[1].getHeight();
        state.putFloat("height", height);
    }

    void setPageCount(final int pageCount) {
        this.pageCount = pageCount;
        if (pageNr >= pageCount)
            pageNr = 0;
        createSheets();
        if (haveSavedState) {
            xPos = savedXPos;
            xPosDraw = savedXPos;
            yPos = savedYPos;
            yPosDraw = savedYPos;
            sheets[1].setHeight(max(savedHeight, surfaceHeight));
        } else {
            xPos = 0f;
            yPos = 0f;
            xPosDraw = 0f;
            yPosDraw = 0f;
        }
        informActivityAboutPageNr();
    }

	void setPasswordNeeded(final boolean passwordNeeded) {
    	knownIfPasswordNeeded = true;
    	if (!passwordNeeded) {
    		authenticated = true;
    		return;
    	}
    	requestPassword();
    }

	private boolean adjust() {
        boolean adjusted = false;
        if (xPosDraw != xPos || yPosDraw != yPos) {
            xPosDraw = adjustPos(xPosDraw, xPos);
            yPosDraw = adjustPos(yPosDraw, yPos);
            adjusted = true;
        }
        if (turnLeftProgressDraw != turnLeftProgress) {
            turnLeftProgressDraw = adjustPos(turnLeftProgressDraw, turnLeftProgress);
            adjusted = true;
        }
        if (turnRightProgressDraw != turnRightProgress) {
            turnRightProgressDraw = adjustPos(turnRightProgressDraw, turnRightProgress);
            adjusted = true;
        }
        if (touched)
            return adjusted;
        final float maxXPos = max((width - surfaceWidth) * 0.5f, 0f);
        final float minXPos = -maxXPos;
        if (xPos > maxXPos) {
            xPos = adjustCoord(xPos, maxXPos);
            adjusted = true;
        } else if (xPos < minXPos) {
            xPos = adjustCoord(xPos, minXPos);
            adjusted = true;
        }
        final float maxYPos = max((height - surfaceHeight) * 0.5f, 0f);
        final float minYPos = -maxYPos;
        if (yPos > maxYPos) {
            yPos = adjustCoord(yPos, maxYPos);
            adjusted = true;
        } else if (yPos < minYPos) {
            yPos = adjustCoord(yPos, minYPos);
            adjusted = true;
        }
        if (flingLeft == 0f) {
            if (turnLeftProgress > 0f) {
                turnLeftProgress = adjustCoord(turnLeftProgress, 0f);
                adjusted = true;
            }
        }
        if (flingRight == 0f) {
            if (turnRightProgress > 0f) {
                turnRightProgress = adjustCoord(turnRightProgress, 0f);
                adjusted = true;
            }
        }
        if (height < surfaceHeight && width < surfaceWidth) {
            if (height * surfaceWidth > width * surfaceHeight) {
                height = adjustValueInt(height, surfaceHeight, COORD_ADJUST_SPEED);
            } else {
                final float aspect = height / width;
                width = adjustValueInt(width, surfaceWidth, COORD_ADJUST_SPEED);
                height = width * aspect;
            }
            sheets[1].setHeight(height);
            width = sheets[1].getWidth();
            adjusted = true;
        }
        if (xPosDraw == xPos) {
            final float nx = (int) xPos;
            if (nx != xPos) {
                xPos = nx;
                xPosDraw = nx;
                adjusted = true;
            }
        }
        if (yPosDraw == yPos) {
            final float ny = (int) yPos;
            if (ny != yPos) {
                yPos = ny;
                yPosDraw = ny;
                adjusted = true;
            }
        }
        return adjusted;
    }

    private void authenticate(final String password) {
    	docRenderer.requestAuthenticate(this, password, new ProcBoolean() {
			@Override
			public void invoke(final boolean authenticated) {
				passwordResponse = passwordRequest;
				DocControl.this.authenticated = authenticated;
			}
    	});
	}

    private boolean canTurnLeft() {
    	return pageNr > 0;
    }
    
    private boolean canTurnRight() {
    	if (pageMode == PAGE_MODE_ONE_PAGE)
    		return pageNr + 1 < pageCount;
    	if (pageMode == PAGE_MODE_TWO_PAGES_WITH_TITLE)
    		return pageNr == 0 && pageCount >= 2 || pageNr + 2 < pageCount;
    	return pageNr + 2 < pageCount;
    }
    
    private void checkForTurn() {
        if (turnLeftProgress >= 0.5f * surfaceWidth)
            turnLeft();
        else if (turnRightProgress >= 0.5f * surfaceWidth)
            turnRight();
    }

    private void computeTouch(final Touch touch) {
        final float[] xs = touch.getX();
        final float[] ys = touch.getY();
        final int count = xs.length;
        float sumX = 0f;
        float sumY = 0f;
        for (int n = 0; n < count; ++n) {
            final float x = xs[n];
            final float y = ys[n];
            sumX += x;
            sumY += y;
        }
        touchCenterX = sumX / count;
        touchCenterY = sumY / count;
        if (count == 1) {
            touchRadius = 0f;
            return;
        }
        touchRadius = MIN_TOUCH_RADIUS;
        for (int n = 0; n < count; ++n) {
            final float dx = xs[n] - touchCenterX;
            final float dy = ys[n] - touchCenterY;
            final float r = (float) Math.sqrt(dx * dx + dy * dy);
            touchRadius = max(touchRadius, r);
        }
        touchCenterX -= surfaceWidth * 0.5f;
        touchCenterY -= surfaceHeight * 0.5f;
    }

    private void createSheets() {
        for (int k = 0; k < 3; ++k) {
            final int n = (k + 1) % 3; // sequence 1, 2, 0
            if (sheets[n] != null)
                continue;
            final int pnr;
            switch (pageNr) {
            case 0:
            	if (pageMode == PAGE_MODE_TWO_PAGES_WITHOUT_TITLE)
            		pnr = 2 * n - 2;
            	else
            		pnr = n - 1;
                break;
            case 1:
            	if (pageMode == PAGE_MODE_ONE_PAGE)
            		pnr = n;
            	else
            		pnr = n == 0 ? 0 : 2 * n - 1;
                break;
            default:
            	if (pageMode == PAGE_MODE_ONE_PAGE)
            		pnr = pageNr + n - 1;
            	else
                    pnr = pageNr + 2 * n - 2;
                break;
            }
            if (pnr < 0 || pnr >= pageCount)
                continue;
            if (pageMode == PAGE_MODE_ONE_PAGE || (pnr == 0 && pageMode == PAGE_MODE_TWO_PAGES_WITH_TITLE)
            		|| pnr + 1 == pageCount)
                sheets[n] = new Sheet(glGeneration, texturePool, directByteBufferPool, docRenderer, pnr, -1);
            else
                sheets[n] = new Sheet(glGeneration, texturePool, directByteBufferPool, docRenderer, pnr, pnr + 1);
            sheets[n].setHeight(surfaceHeight);
        }
    }

    private void informActivityAboutPageNr() {
    	final int pnr = pageNr;
    	final int pc = pageCount;
    	activity.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				activity.setPageNr(pnr, pc);
			}
		});
	}

    private void motion(float dx, float dy, float zoom, final float zoomX, final float zoomY) {
    	activity.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				activity.onMotion();
			}
		});
        if (dx > 0f && turnRightProgress > 0f) {
            if (turnRightProgress > dx) {
                turnRightProgress -= dx;
                dx = 0f;
            } else {
                dx -= turnRightProgress;
                turnRightProgress = 0f;
            }
        }
        if (dx < 0f && turnLeftProgress > 0f) {
            if (turnLeftProgress > -dx) {
                turnLeftProgress += dx;
                dx = 0f;
            } else {
                dx += turnLeftProgress;
                turnLeftProgress = 0f;
            }
        }
        final float newXPos = xPos + dx;
        final float maxXPos = max((width - surfaceWidth) * 0.5f, 0f);
        final float minXPos = -maxXPos;
        if (dx < 0f && newXPos < minXPos && canTurnRight()) {
            if (xPos > minXPos)
                xPos = -maxXPos;
            turnRightProgress += xPos - newXPos;
            dx = 0f;
        } else if (dx > 0f && newXPos > maxXPos && canTurnLeft()) {
            if (xPos < maxXPos)
                xPos = maxXPos;
            turnLeftProgress += newXPos - xPos;
            dx = 0f;
        }
        if (dx > 0f) {
            if (xPos - maxXPos > MOTION_RESISTANCE_START)
                xPos += dx / ((xPos - maxXPos) * MOTION_RESISTANCE);
            else
                xPos += dx;
        } else if (dx < 0f) {
            if (minXPos - xPos > MOTION_RESISTANCE_START)
                xPos += dx / ((minXPos - xPos) * MOTION_RESISTANCE);
            else
                xPos += dx;
        }
        final float maxYPos = max((height - surfaceHeight) * 0.5f, 0f);
        final float minYPos = -maxYPos;
        if (dy > 0f) {
            if (yPos - maxYPos > MOTION_RESISTANCE_START)
                yPos += dy / ((yPos - maxYPos) * MOTION_RESISTANCE);
            else
                yPos += dy;
        } else if (dy < 0f) {
            if (minYPos - yPos > MOTION_RESISTANCE_START)
                yPos += dy / ((minYPos - yPos) * MOTION_RESISTANCE);
            else
                yPos += dy;
        }
        if (zoom > 0f) {
            float h = height * zoom;
            h = max(h, MINIMUM_HEIGHT);
            h = min(h, 2048f);
            zoom = h / height;
            sheets[1].setHeight(h);
            xPos = zoom * xPos + (1f - zoom) * zoomX;
            yPos = zoom * yPos + (1f - zoom) * zoomY;
        }
    }

	private int normalizePageNr(int pnr) {
		if (pnr >= pageCount)
			pnr = pageCount - 1;
		if (pnr < 0)
			pnr = 0;
		switch (pageMode) {
		case PAGE_MODE_ONE_PAGE:
			break;
		case PAGE_MODE_TWO_PAGES_WITH_TITLE:
			if (pnr > 0 && pnr % 2 == 0)
				--pnr;
			break;
		default:
			// PAGE_MODE_TWO_PAGES_WITHOUT_TITLE:
			if (pnr > 0 && pnr % 2 == 1)
				--pnr;
		}
		return pnr;
	}

	private void onFling(final Fling fling) {
        if (fling.getDuration() > MAX_FLING_DURATION || flingLeft > 0f || flingRight > 0f)
            return;
        final float velX = fling.getVelocityX();
        final float velY = abs(fling.getVelocityY());
        if (velX >= MIN_FLING_VELOCITY && velX * 0.5f >= velY) {
            if (canTurnLeft())
                flingLeft = velX;
        } else if (velX <= -MIN_FLING_VELOCITY && velX * -0.5f >= velY) {
            if (canTurnRight())
                flingRight = -velX;
        }
    }

    private void onTouch(final Touch touch) {
        switch (touch.getAction()) {
        case MotionEvent.ACTION_UP:
            checkForTurn();
            lastTouchWasMove = false;
            touched = false;
            break;
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_POINTER_DOWN:
            touched = true;
        case MotionEvent.ACTION_POINTER_UP:
            lastTouchWasMove = false;
            break;
        case MotionEvent.ACTION_MOVE:
            touchMove(touch);
            lastTouchWasMove = true;
            break;
        default:
        	// nothing
        }
    }

    private void touchMove(final Touch touch) {
        computeTouch(touch);
        if (lastTouchWasMove) {
            float zoom = prevTouchRadius == 0f ? 0f : touchRadius / prevTouchRadius;
            motion(touchCenterX - prevTouchCenterX, touchCenterY - prevTouchCenterY, zoom, touchCenterX, touchCenterY);
        }
        prevTouchCenterX = touchCenterX;
        prevTouchCenterY = touchCenterY;
        prevTouchRadius = touchRadius;
    }

    private void requestPassword() {
    	if (passwordRequest > passwordResponse)
    		return;
    	++passwordRequest;
		activity.runOnUiThread(new Runnable() {
			@Override
			public void run() {
				activity.requestPassword();
			}
		});
	}

    private void requestPageCount() {
        if (pageCountRequested)
            return;
        docRenderer.requestPageCount(this, new ProcInt() {
            @Override
            public void invoke(final int pageCount) {
                setPageCount(pageCount);
            }
        });
        pageCountRequested = true;
    }

    private void requestPasswordNeeded() {
        if (passwordNeededRequested)
            return;
        docRenderer.requestPasswordNeeded(this, new ProcBoolean() {
            @Override
            public void invoke(final boolean passwordNeeded) {
                setPasswordNeeded(passwordNeeded);
            }
        });
        passwordNeededRequested = true;
    }

    private void turnLeft() {
        if (sheets[2] != null)
            sheets[2].dispose(glGeneration);
        sheets[2] = sheets[1];
        sheets[1] = sheets[0];
        sheets[0] = null;
        pageNr -= pageMode == PAGE_MODE_ONE_PAGE ? 1 : 2;
        if (pageNr < 0)
            pageNr = 0;
        informActivityAboutPageNr();
        createSheets();
        sheets[2].setHeight(surfaceHeight);
        xPos = turnLeftProgress - (surfaceWidth + sheets[1].getWidth()) * 0.5f;
        xPosDraw = turnLeftProgressDraw - (surfaceWidth + sheets[1].getWidth()) * 0.5f;
        yPos = 0;
        yPosDraw = 0;
        turnLeftProgress = 0f;
        turnLeftProgressDraw = 0f;
        turnRightProgress = 0f;
        turnRightProgressDraw = 0f;
        flingLeft = 0f;
        flingRight = 0f;
    }

    private void turnRight() {
        if (sheets[0] != null)
            sheets[0].dispose(glGeneration);
        sheets[0] = sheets[1];
        sheets[1] = sheets[2];
        sheets[2] = null;
        switch (pageMode) {
        case PAGE_MODE_TWO_PAGES_WITH_TITLE:
	        if (pageNr == 0)
	            pageNr = 1;
	        else
	            pageNr += 2;
	        break;
        case PAGE_MODE_TWO_PAGES_WITHOUT_TITLE:
            pageNr += 2;
            break;
        default:
        	// PAGE_MODE_ONE_PAGE
        	++pageNr;
        }
        informActivityAboutPageNr();
        createSheets();
        sheets[0].setHeight(surfaceHeight);
        xPos = (surfaceWidth + sheets[1].getWidth()) * 0.5f - turnRightProgress;
        xPosDraw = (surfaceWidth + sheets[1].getWidth()) * 0.5f - turnRightProgressDraw;
        yPos = 0;
        yPosDraw = 0;
        turnLeftProgress = 0f;
        turnLeftProgressDraw = 0f;
        turnRightProgress = 0f;
        turnRightProgressDraw = 0f;
        flingLeft = 0f;
        flingRight = 0f;
    }
}
